package com.android.gpuimage;

import android.graphics.Bitmap;
import android.opengl.GLES20;
import android.support.v4.view.MotionEventCompat;
import android.support.v4.view.ViewCompat;

import com.android.av.GlUtil;

import java.nio.IntBuffer;

/**
 * Camera 的美颜滤镜第二步
 */

public class BeautySub2Filter extends GPUImageMultiInputFilter {

    private static final String VERTEX_SHADER = "uniform highp mat4 uTexMatrix; \n" +
            "  attribute vec4 position;\n" +
            "  attribute highp vec4 inputTextureCoordinate;\n" +
            "  uniform highp float texelWidthOffset; \n" +
            "  uniform highp float texelHeightOffset;\n" +
            "  uniform highp float blurSize;\n" +
            "  \n" +
            "  varying highp vec2 textureCoordinate;\n" +
            "  varying highp vec2 blurCoordinates[7];\n" +
            " void main() {\n" +
            "  \tgl_Position = position;\n" +
            "  \ttextureCoordinate =  (uTexMatrix * inputTextureCoordinate).xy;\n" +
            "   highp vec2 singleStepOffset = vec2( texelWidthOffset,texelHeightOffset ) ;\n" +
            "blurCoordinates[0] = textureCoordinate;\n" +
            " blurCoordinates[1] = textureCoordinate + singleStepOffset * 1.458429;\n" +
            " blurCoordinates[2] = textureCoordinate - singleStepOffset * 1.458429;\n" +
            " blurCoordinates[3] = textureCoordinate + singleStepOffset * 3.403985;\n" +
            " blurCoordinates[4] = textureCoordinate - singleStepOffset * 3.403985;\n" +
            " blurCoordinates[5] = textureCoordinate + singleStepOffset * 5.351806;\n" +
            " blurCoordinates[6] = textureCoordinate - singleStepOffset * 5.351806;\n" +
            "}  ";

    private static final String FRAGEMENT_SHADER = "varying highp vec2 textureCoordinate;\n" +
            " \n" +
            "  uniform highp float texelWidthOffset; \n" +
            "  uniform highp float texelHeightOffset;\n" +
            " varying highp vec2 blurCoordinates[7];\n" +
            "uniform int enableBlur;\n" +
            "uniform lowp float blurRadiusFromCenter;\n" +
            "uniform int enableVirtual;\n" +
            "uniform sampler2D inputImageTextureVirtual;\n" +
            " uniform sampler2D inputImageTexture;\n" +
            " uniform sampler2D inputImageTexture2;\n" +
            " uniform lowp float alphaValue;\n" +
            " uniform lowp float softenLevel;\n" +
            " \n" +
            " void main()\n" +
            " {\n" +
            "     mediump vec4 yuv = texture2D(inputImageTexture, textureCoordinate);\n" +
            "     \n" +
            " mediump float sum = 0.0; \n" +
            "  \n" +
            "     \n" +
            " mediump float sumr = 0.0; \n" +
            "  \n" +
            "     \n" +
            " mediump float sumg = 0.0; \n" +
            "  \n" +
            "     \n" +
            " mediump float sumb = 0.0; \n" +
            "  \n" +
            " sum += texture2D(inputImageTexture, blurCoordinates[0]).a * 0.137023; \n" +
            " sumr += texture2D(inputImageTexture, blurCoordinates[0]).r * 0.137023; \n" +
            " sumg += texture2D(inputImageTexture, blurCoordinates[0]).g * 0.137023; \n" +
            " sumb += texture2D(inputImageTexture, blurCoordinates[0]).b * 0.137023; \n" +
            " sum += texture2D(inputImageTexture, blurCoordinates[1]).a * 0.239337; \n" +
            " sum += texture2D(inputImageTexture, blurCoordinates[2]).a * 0.239337; \n" +
            " sumr += texture2D(inputImageTexture, blurCoordinates[1]).r * 0.239337; \n" +
            " sumr += texture2D(inputImageTexture, blurCoordinates[2]).r * 0.239337; \n" +
            " sumg += texture2D(inputImageTexture, blurCoordinates[1]).g * 0.239337; \n" +
            " sumg += texture2D(inputImageTexture, blurCoordinates[2]).g * 0.239337; \n" +
            " sumb += texture2D(inputImageTexture, blurCoordinates[1]).b * 0.239337; \n" +
            " sumb += texture2D(inputImageTexture, blurCoordinates[2]).b * 0.239337; \n" +
            " sum += texture2D(inputImageTexture, blurCoordinates[3]).a * 0.139440; \n" +
            " sum += texture2D(inputImageTexture, blurCoordinates[4]).a * 0.139440; \n" +
            " sumr += texture2D(inputImageTexture, blurCoordinates[3]).r * 0.139440; \n" +
            " sumr += texture2D(inputImageTexture, blurCoordinates[4]).r * 0.139440; \n" +
            " sumg += texture2D(inputImageTexture, blurCoordinates[3]).g * 0.139440; \n" +
            " sumg += texture2D(inputImageTexture, blurCoordinates[4]).g * 0.139440; \n" +
            " sumb += texture2D(inputImageTexture, blurCoordinates[3]).b * 0.139440; \n" +
            " sumb += texture2D(inputImageTexture, blurCoordinates[4]).b * 0.139440; \n" +
            " sum += texture2D(inputImageTexture, blurCoordinates[5]).a * 0.052711; \n" +
            " sum += texture2D(inputImageTexture, blurCoordinates[6]).a * 0.052711; \n" +
            " sumr += texture2D(inputImageTexture, blurCoordinates[5]).r * 0.052711; \n" +
            " sumr += texture2D(inputImageTexture, blurCoordinates[6]).r * 0.052711; \n" +
            " sumg += texture2D(inputImageTexture, blurCoordinates[5]).g * 0.052711; \n" +
            " sumg += texture2D(inputImageTexture, blurCoordinates[6]).g * 0.052711; \n" +
            " sumb += texture2D(inputImageTexture, blurCoordinates[5]).b * 0.052711; \n" +
            " sumb += texture2D(inputImageTexture, blurCoordinates[6]).b * 0.052711; \n" +
            " mediump float delta = yuv.g - sum;\n" +
            " mediump float alpha = 0.0;   \n" +
            " if (delta < 0.0){ \n" +
            "   alpha = 8.0 * pow((delta + 0.5), 4.0); \n" +
            " } else {   \n" +
            "   alpha = 1.0 - 8.0 * pow((0.5 - delta), 4.0); \n" +
            " } \n" +
            "  lowp float lightr = texture2D(inputImageTexture2, vec2(yuv.r, 0.5)).r;    \n" +
            " lowp float lightg = texture2D(inputImageTexture2, vec2(yuv.g, 0.5)).g;    \n" +
            " lowp float lightb = texture2D(inputImageTexture2, vec2(yuv.b, 0.5)).b;    \n" +
            " lowp float rrr = mix(lightr, yuv.r, alpha);    \n" +
            " lowp float ggg = mix(lightg, yuv.g, alpha);    \n" +
            " lowp float bbb = mix(lightb, yuv.b, alpha);    \n" +
            "    \n" +
            " mediump vec4 tc = vec4(rrr, ggg, bbb, 1.0); tc = mix(yuv, tc, softenLevel);\n" +
            " if (enableBlur == 1) {\n" +
            "\tlowp float xdistance = distance(textureCoordinate, vec2(0.5));\n" +
            "\tif (xdistance > blurRadiusFromCenter) {\n" +
            "\t    tc.rgb = mix(tc.rgb, vec3(sumr, sumg, sumb), clamp((xdistance-blurRadiusFromCenter)*10.0, 0.0, 0.7));\n" +
            "\t  }\n" +
            "\t}\n" +
            " mediump vec4 virtual_yuv = texture2D(inputImageTextureVirtual, textureCoordinate);\n" +
            " if (enableVirtual == 1) {\n" +
            "\ttc = vec4(tc.rgb*virtual_yuv.rgb, 0.0);\n" +
            " }\n" +
            " gl_FragColor = vec4(tc.rgb, 1.0); \n" +
            " }";

    private int mUTexMatrixLocation;
    private int mTexelWidthOffsetLocation;
    private int mTexelHeightOffsetLocation;

    private int mEnableBlurLocation;
    private int mBlurRadiusFromCenterLocation;
    private int mEnableVirtualLocation;
    private int mAlphaValueLocation;
    private int mSoftenLevelLocation;

    private boolean mPositive;

    private boolean mEnableBlur = false;
    private float mBlurRadiusFromCenter = 0.4f;
    private boolean mEnableVirtual = false;
    private Bitmap mVirtualTextureBitmap;
    private Bitmap mTexture2Bitmap;
    private float mAlphaValue = 0.66f;
    private float mSoftenLevel = 0.5f;

    public BeautySub2Filter(int floor, int level) {
        this(floor, level, false, false, 0.4f, false, null, null,
                0.66f, 0.5f);
    }

    public BeautySub2Filter(int floor, int level, boolean positive, boolean enableBlur,
                            float blurRadiusFromCenter, boolean enableVirtual,
                            Bitmap virtualTextureBitmap, Bitmap texture2Bitmap,
                            float alphaValue, float softenLevel) {
        super(BeautyFilter.getVertexShader(floor, level),
                BeautyFilter.getFragmentShader1(floor, level),
                new String[] {"inputImageTexture2", "inputImageTextureVirtual"});
        mPositive = positive;
        mEnableBlur = enableBlur;
        mBlurRadiusFromCenter = blurRadiusFromCenter;
        mEnableVirtual = enableVirtual;
        mVirtualTextureBitmap = virtualTextureBitmap;
        mTexture2Bitmap = texture2Bitmap;
        if (mTexture2Bitmap == null) {
            mTexture2Bitmap = createTexture2Bitmap();
        }
        mAlphaValue = alphaValue;
        mSoftenLevel = softenLevel;
    }

    @Override
    public void onInit() {
        super.onInit();
        mUTexMatrixLocation = GLES20.glGetUniformLocation(getProgram(), "uTexMatrix");
        mTexelWidthOffsetLocation = GLES20.glGetUniformLocation(getProgram(), "texelWidthOffset");
        mTexelHeightOffsetLocation = GLES20.glGetUniformLocation(getProgram(), "texelHeightOffset");
        mEnableBlurLocation = GLES20.glGetUniformLocation(getProgram(), "enableBlur");
        mBlurRadiusFromCenterLocation = GLES20.glGetUniformLocation(getProgram(), "blurRadiusFromCenter");
        mEnableVirtualLocation = GLES20.glGetUniformLocation(getProgram(), "enableVirtual");
        mAlphaValueLocation = GLES20.glGetUniformLocation(getProgram(), "alphaValue");
        mSoftenLevelLocation = GLES20.glGetUniformLocation(getProgram(), "softenLevel");
    }

    @Override
    public void onInitialized() {
        super.onInitialized();
        setUniformMatrix4f(mUTexMatrixLocation, GlUtil.IDENTITY_MATRIX);

        setInteger(mEnableBlurLocation, mEnableBlur ? 1 : 0);
        setFloat(mBlurRadiusFromCenterLocation, mBlurRadiusFromCenter);
        setInteger(mEnableVirtualLocation, mEnableVirtual ? 1 : 0);
        setFloat(mAlphaValueLocation, mAlphaValue);
        setFloat(mSoftenLevelLocation, mSoftenLevel);
        if (mTexture2Bitmap != null) {
            setBitmap(1, mTexture2Bitmap);
        }
        if (mVirtualTextureBitmap != null) {
            setBitmap(2, mVirtualTextureBitmap);
        }
    }

    @Override
    public void onOutputSizeChanged(int width, int height) {
        super.onOutputSizeChanged(width, height);
        if (mPositive) {
            setFloat(mTexelWidthOffsetLocation, 0f);
            setFloat(mTexelHeightOffsetLocation, (((float) width) * 0.0033333334f) / ((float) height));
        } else {
            setFloat(mTexelWidthOffsetLocation, 0f);
            setFloat(mTexelHeightOffsetLocation, (((float) width) * 0.0033333334f) / ((float) height));
        }
    }

    public Bitmap createTexture2Bitmap() {
        Bitmap createBitmap = Bitmap.createBitmap(GLES20.GL_DEPTH_BUFFER_BIT, 1, Bitmap.Config.ARGB_8888);
        int[] iArr = new int[GLES20.GL_DEPTH_BUFFER_BIT];
        for (int i = 0; i < GLES20.GL_DEPTH_BUFFER_BIT; i++) {
            int i2 = ((255 - (((255 - i) * (255 - i)) / MotionEventCompat.ACTION_MASK)) + i) / 2;
            iArr[i] = ((i2 << 16) | ((i2 << 8) | i2)) | ViewCompat.MEASURED_STATE_MASK;
        }
        createBitmap.copyPixelsFromBuffer(IntBuffer.wrap(iArr));
        return createBitmap;
    }
}
